#include "llvm/ExecutionEngine/Orc/LLJIT.h"

#include "llvm/IR/BasicBlock.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/Type.h"
#include "llvm/IR/Value.h"
#include "llvm/IR/Verifier.h"
#include "llvm/Support/TargetSelect.h"

using namespace llvm;
using namespace llvm::orc;

/**
 * llvm创建函数并调用
*/

Function* createSumFunction(Module* module)
{
	/* Builds the following function:
	int sum(int a, int b) {
	    int sum1 = 1 + 1;
	    int sum2 = sum1 + a;
	    int result = sum2 + b;
	    return result;
	}
	*/

	LLVMContext& context = module->getContext();
	IRBuilder<> builder(context);

	// Define function's signature
	std::vector<Type*> Integers(2, builder.getInt32Ty());
	auto* funcType = FunctionType::get(builder.getInt32Ty(), Integers, false);

	// create the function "sum" and bind it to the module with ExternalLinkage,
	// so we can retrieve it later
	auto* sum_func = Function::Create(funcType, Function::ExternalLinkage, "sum", module);

	// Define the entry block and fill it with an appropriate code
	auto* entry = BasicBlock::Create(context, "entry", sum_func);
	builder.SetInsertPoint(entry);

	// Add constant to itself, to visualize constant folding
	Value* constant = ConstantInt::get(builder.getInt32Ty(), 0x1);
	//Value *One = builder.getInt32(1);
	auto* sum1 = builder.CreateAdd(constant, constant, "sum1");

	// Retrieve arguments and proceed with further adding...
	auto args = sum_func->arg_begin();
	Value* arg1 = &(*args);
	args = std::next(args);
	Value* arg2 = &(*args);
	auto* sum2 = builder.CreateAdd(sum1, arg1, "sum2");
	auto* result = builder.CreateAdd(sum2, arg2, "result");

	// ...and return
	builder.CreateRet(result);

	// Verify at the end
	verifyFunction(*sum_func);

	return sum_func;
};


int main()
{
	// 初始化LLVM
	InitializeNativeTarget();
	InitializeNativeTargetAsmPrinter();
	InitializeNativeTargetAsmParser();

	// 创建一个LLJIT实例
	auto JIT = cantFail(LLJITBuilder().create());

	// 创建一个新的LLVM模块
	orc::ThreadSafeContext ctx(std::make_unique<LLVMContext>());
	LLVMContext* context = ctx.getContext();
	auto module = std::make_unique<Module>("test", *context);
	module->setDataLayout(JIT->getDataLayout());

	// 创建一个简单的函数
	// Define function's signature
	auto* func = createSumFunction(module.get());

	module->dump();

	// 将模块添加到LLJIT
	auto tsm = ThreadSafeModule(std::move(module), ctx);
	cantFail(JIT->addIRModule(std::move(tsm)));

	// 查找并执行函数
	auto sym = JIT->lookup("sum");
	assert(sym && "Function not found");

	int (*FP)(int, int) = (int (*)(int, int))(sym->getAddress());
	int result = FP(11, 12);

	outs() << "result : 1 + 1 + 11 + 12 = " << result << "\n";
	assert(result == 25 && "Function returned incorrect result");

	return 0;
}
